use common_observing::tracing::log::error;
use tonic::{self, transport::Server, Request, Response};

use common_base::{
    prelude::*,
    tokio::{self, task::JoinHandle},
};

use crate::{
    data::{DataObject, ParamObject},
    invoker::remote::remote_invoke::{
        remote_invoker_server::{RemoteInvoker, RemoteInvokerServer},
        InvokeRequest, InvokeResponse,
    },
    EaiServer, InvokerManager,
};

#[derive(Default)]
pub struct InvokerServer;

impl InvokerServer {
    pub fn initialize(eai_server: &EaiServer) -> Result<JoinHandle<()>> {
        let addr = eai_server.get_eai_server_listen_uri().parse()?;
        let invoker_server = InvokerServer::default();
        Ok(tokio::spawn(async move {
            if let Err(e) = Server::builder()
                .add_service(RemoteInvokerServer::new(invoker_server))
                .serve(addr)
                .await
            {
                error!("grpc service quit with error: {}", e);
            }
        }))
    }
}

#[tonic::async_trait]
impl RemoteInvoker for InvokerServer {
    async fn invoke(
        &self,
        request: Request<InvokeRequest>,
    ) -> std::result::Result<Response<InvokeResponse>, tonic::Status> {
        let invoke_request = request.into_inner();

        let invoker_name = invoke_request
            .active_invoker_name
            .as_str()
            .try_into()
            .map_err(|e: ErrorCode| tonic::Status::new(tonic::Code::Unavailable, e.message()))?;
        let param_object = bincode::deserialize::<ParamObject>(&invoke_request.param_object)
            .map_err(|e| tonic::Status::new(tonic::Code::InvalidArgument, e.to_string()))?;
        let data_object = if let Some(data_obj) = invoke_request.data_object {
            Some(
                bincode::deserialize::<DataObject>(&data_obj)
                    .map_err(|e| tonic::Status::new(tonic::Code::InvalidArgument, e.to_string()))?,
            )
        } else {
            None
        };

        let invoke_response =
            match InvokerManager::iom(invoker_name, param_object, data_object).await {
                Ok(response) => {
                    let response_object_bytes = bincode::serialize(&response)
                        .map_err(|e| tonic::Status::new(tonic::Code::Internal, e.to_string()))?;
                    InvokeResponse {
                        code: 1,
                        message: "success".to_owned(),
                        response_object_bytes,
                    }
                }
                Err(e) => InvokeResponse {
                    code: 0,
                    message: format!("invoker internal error, cause: {}", e),
                    response_object_bytes: Vec::default(),
                },
            };

        Ok(Response::new(invoke_response))
    }
}
